\subsection{Таблица \TT{X\$KSMLRU} в \oracle}
\myindex{\oracle}

В заметке \IT{Diagnosing and Resolving Error ORA-04031 
on the Shared Pool or Other Memory Pools [Video] [ID 146599.1]} упоминается некая служебная таблица:

\begin{framed}
\begin{quotation}
There is a fixed table called X\$KSMLRU that tracks allocations in the shared pool that cause other objects 
in the shared pool to be aged out. This fixed table can be used to identify what is causing the large allocation.

If many objects are being periodically flushed from the shared pool then this will cause response time problems 
and will likely cause library cache latch contention problems when the objects are reloaded into the shared pool.

One unusual thing about the X\$KSMLRU fixed table is that the contents of the fixed table are erased whenever 
someone selects from the fixed table. This is done since the fixed table stores only the largest allocations 
that have occurred. The values are reset after being selected so that subsequent large allocations can be noted 
even if they were not quite as large as others that occurred previously. Because of this resetting, the output 
of selecting from this table should be carefully kept since it cannot be retrieved back after the query is issued.
\end{quotation}
\end{framed}

Однако, как можно легко убедиться, эта системная таблица очищается всякий раз, когда кто-то делает запрос 
к ней.
Сможем ли мы найти причину, почему это происходит?
Если вернуться к уже рассмотренным таблицам \TT{kqftab} и \TT{kqftap} полученных при помощи \oracletables, 
содержащим информацию о X\$-таблицах, мы узнаем что для того чтобы подготовить строки этой таблицы, 
вызывается функция \TT{ksmlrs()}:

\begin{lstlisting}[caption=Результат работы \OracleTablesName]
kqftab_element.name: [X$KSMLRU] ?: [ksmlr] 0x4 0x64 0x11 0xc 0xffffc0bb 0x5
kqftap_param.name=[ADDR] ?: 0x917 0x0 0x0 0x0 0x4 0x0 0x0
kqftap_param.name=[INDX] ?: 0xb02 0x0 0x0 0x0 0x4 0x0 0x0
kqftap_param.name=[INST_ID] ?: 0xb02 0x0 0x0 0x0 0x4 0x0 0x0
kqftap_param.name=[KSMLRIDX] ?: 0xb02 0x0 0x0 0x0 0x4 0x0 0x0
kqftap_param.name=[KSMLRDUR] ?: 0xb02 0x0 0x0 0x0 0x4 0x4 0x0
kqftap_param.name=[KSMLRSHRPOOL] ?: 0xb02 0x0 0x0 0x0 0x4 0x8 0x0
kqftap_param.name=[KSMLRCOM] ?: 0x501 0x0 0x0 0x0 0x14 0xc 0x0
kqftap_param.name=[KSMLRSIZ] ?: 0x2 0x0 0x0 0x0 0x4 0x20 0x0
kqftap_param.name=[KSMLRNUM] ?: 0x2 0x0 0x0 0x0 0x4 0x24 0x0
kqftap_param.name=[KSMLRHON] ?: 0x501 0x0 0x0 0x0 0x20 0x28 0x0
kqftap_param.name=[KSMLROHV] ?: 0xb02 0x0 0x0 0x0 0x4 0x48 0x0
kqftap_param.name=[KSMLRSES] ?: 0x17 0x0 0x0 0x0 0x4 0x4c 0x0
kqftap_param.name=[KSMLRADU] ?: 0x2 0x0 0x0 0x0 0x4 0x50 0x0
kqftap_param.name=[KSMLRNID] ?: 0x2 0x0 0x0 0x0 0x4 0x54 0x0
kqftap_param.name=[KSMLRNSD] ?: 0x2 0x0 0x0 0x0 0x4 0x58 0x0
kqftap_param.name=[KSMLRNCD] ?: 0x2 0x0 0x0 0x0 0x4 0x5c 0x0
kqftap_param.name=[KSMLRNED] ?: 0x2 0x0 0x0 0x0 0x4 0x60 0x0
kqftap_element.fn1=ksmlrs
kqftap_element.fn2=NULL
\end{lstlisting}

\myindex{tracer}
Действительно, при помощи \tracer легко убедиться, что эта функция вызывается каждый раз, когда мы обращаемся 
к таблице \TT{X\$KSMLRU}.

\myindex{\CStandardLibrary!memset()}
Здесь есть ссылки на функции \TT{ksmsplu\_sp()} и \TT{ksmsplu\_jp()}, каждая из которых в итоге вызывает 
\TT{ksmsplu()}.
В конце функции \TT{ksmsplu()} мы видим вызов \TT{memset()}:

\begin{lstlisting}[caption=ksm.o,style=customasmx86]
...

.text:00434C50 loc_434C50:    ; DATA XREF: .rdata:off_5E50EA8
.text:00434C50         mov     edx, [ebp-4]
.text:00434C53         mov     [eax], esi
.text:00434C55         mov     esi, [edi]
.text:00434C57         mov     [eax+4], esi
.text:00434C5A         mov     [edi], eax
.text:00434C5C         add     edx, 1
.text:00434C5F         mov     [ebp-4], edx
.text:00434C62         jnz     loc_434B7D
.text:00434C68         mov     ecx, [ebp+14h]
.text:00434C6B         mov     ebx, [ebp-10h]
.text:00434C6E         mov     esi, [ebp-0Ch]
.text:00434C71         mov     edi, [ebp-8]
.text:00434C74         lea     eax, [ecx+8Ch]
.text:00434C7A         push    370h            ; Size
.text:00434C7F         push    0               ; Val
.text:00434C81         push    eax             ; Dst
.text:00434C82         call    __intel_fast_memset
.text:00434C87         add     esp, 0Ch
.text:00434C8A         mov     esp, ebp
.text:00434C8C         pop     ebp
.text:00434C8D         retn
.text:00434C8D _ksmsplu  endp
\end{lstlisting}

Такие конструкции (\TT{memset (block, 0, size)}) очень часто используются для простого обнуления блока памяти.
Мы можем попробовать рискнуть, заблокировав вызов \TT{memset()} и посмотреть, что будет?

\myindex{tracer}
Запускаем \tracer со следующей опцией: поставить точку останова на \TT{0x434C7A} 
(там, где начинается передача параметров для функции \TT{memset()}) так, 
чтобы \tracer в этом месте установил указатель инструкций процессора (\TT{EIP}) на место, где уже произошла очистка переданных параметров в \TT{memset()} (по адресу \TT{0x434C8A}):

Можно сказать, при помощи этого, мы симулируем безусловный переход с адреса \TT{0x434C7A} на \TT{0x434C8A}.

\begin{lstlisting}
tracer -a:oracle.exe bpx=oracle.exe!0x00434C7A,set(eip,0x00434C8A)
\end{lstlisting}

(Важно: все эти адреса справедливы только для win32-версии \oracle 11.2)

Действительно, после этого мы можем обращаться к таблице \TT{X\$KSMLRU} сколько угодно, и она уже не очищается!

\sout{Не делайте этого дома ("Разрушители легенд")} Не делайте этого на своих production-серверах.

Впрочем, это не обязательно полезное или желаемое поведение системы, но как эксперимент по поиску нужного кода, нам это подошло!


